package minio

import (
	"context"
	"fmt"
	"mime/multipart"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/example/video_backend/pkg/logger"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

var client *minio.Client
var bucketName string
var location string

// InitMinIO 初始化MinIO客户端
func InitMinIO() error {
	// 获取配置信息
	endpoint := viper.GetString("minio.endpoint")
	accessKey := viper.GetString("minio.access_key")
	secretKey := viper.GetString("minio.secret_key")
	useSSL := viper.GetBool("minio.use_ssl")
	bucketName = viper.GetString("minio.bucket_name")
	location = viper.GetString("minio.location")

	// 创建MinIO客户端
	var err error
	client, err = minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
		Secure: useSSL,
	})

	if err != nil {
		logger.Error("Failed to initialize MinIO client: " + err.Error())
		return err
	}

	// 检查桶是否存在
	exists, err := client.BucketExists(context.Background(), bucketName)
	if err != nil {
		logger.Error("Failed to check bucket existence: " + err.Error())
		return err
	}

	// 如果桶不存在，创建桶
	if !exists {
		err = client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{
			Region: location,
		})
		if err != nil {
			logger.Error("Failed to create bucket: " + err.Error())
			return err
		}
		logger.Info("Created MinIO bucket: "+bucketName, zap.String("location", location))
	}

	logger.Info("MinIO client initialized successfully", zap.String("bucket", bucketName))
	return nil
}

// UploadFile 上传文件到MinIO
func UploadFile(ctx context.Context, file *multipart.FileHeader, objectPrefix string) (string, error) {
	if client == nil {
		return "", fmt.Errorf("MinIO client is not initialized")
	}

	// 打开文件
	src, err := file.Open()
	if err != nil {
		logger.Error("Failed to open file: " + err.Error())
		return "", err
	}
	defer src.Close()

	// 生成唯一的对象名称
	timestamp := time.Now().UnixNano()
	ext := filepath.Ext(file.Filename)
	fileName := strings.TrimSuffix(file.Filename, ext)
	objectName := fmt.Sprintf("%s%s_%d%s", objectPrefix, fileName, timestamp, ext)

	// 上传文件
	uploadInfo, err := client.PutObject(ctx, bucketName, objectName, src, file.Size, minio.PutObjectOptions{
		ContentType: "application/octet-stream",
	})

	if err != nil {
		logger.Error("Failed to upload file: " + err.Error())
		return "", err
	}

	logger.Info("File uploaded successfully", zap.String("objectName", objectName), zap.Int64("size", uploadInfo.Size))

	// 生成可访问的URL
	url, err := GetFileURL(ctx, objectName)
	if err != nil {
		return "", err
	}

	return url, nil
}

// GetFileURL 获取文件的可访问URL
func GetFileURL(ctx context.Context, objectName string) (string, error) {
	if client == nil {
		return "", fmt.Errorf("MinIO client is not initialized")
	}

	// 设置URL的过期时间（7天）
	obs := strings.Split(objectName, "/")
	obList := make([]string, len(obs))
	for i, ob := range obs {
		obList[i] = ob
		if i == len(obs)-1 {
			obList[i] = url.QueryEscape(ob)
		}
	}
	return "http://" + client.EndpointURL().Host + "/videos/" + strings.Join(obList, "/"), nil
}

// DeleteFile 从MinIO删除文件
func DeleteFile(ctx context.Context, objectName string) error {
	if client == nil {
		return fmt.Errorf("MinIO client is not initialized")
	}

	// 获取文件的实际路径（去掉URL部分，只保留对象名称）
	objectName = extractObjectName(objectName)

	// 删除文件
	err := client.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{})
	if err != nil {
		logger.Error("Failed to delete file: " + err.Error())
		return err
	}

	logger.Info("File deleted successfully", zap.String("objectName", objectName))

	return nil
}

func SetBucketPolicy(ctx context.Context, bucketName, newPolicy string) error {
	return client.SetBucketPolicy(context.Background(), bucketName, newPolicy) // 使用nil作为policy参数，因为我们直接设置了新的ACL策略
}

// FileExists 检查文件是否存在
func FileExists(ctx context.Context, objectName string) (bool, error) {
	if client == nil {
		return false, fmt.Errorf("MinIO client is not initialized")
	}

	// 获取文件的实际路径
	objectName = extractObjectName(objectName)

	// 尝试获取文件信息
	_, err := client.StatObject(ctx, bucketName, objectName, minio.StatObjectOptions{})
	if err != nil {
		// 如果返回的是对象不存在的错误，返回false
		if minio.ToErrorResponse(err).Code == "NoSuchKey" {
			return false, nil
		}
		// 其他错误，返回错误
		logger.Error("Failed to check file existence: " + err.Error())
		return false, err
	}

	// 文件存在
	return true, nil
}

// extractObjectName 从URL中提取对象名称
func extractObjectName(url string) string {
	// 简化处理：直接返回URL，因为我们在存储时已经使用了完整的对象名称
	return url
}

// UploadImageFile 上传本地图片文件到MinIO
// imagePath: 本地图片文件路径
// objectPrefix: 对象前缀
func UploadImageFile(ctx context.Context, imagePath string, objectPrefix string) (string, error) {
	if client == nil {
		return "", fmt.Errorf("MinIO client is not initialized")
	}

	// 打开图片文件
	src, err := os.Open(imagePath)
	if err != nil {
		logger.Error("Failed to open image file: " + err.Error())
		return "", err
	}
	defer src.Close()

	// 获取文件信息以确定大小
	fileInfo, err := os.Stat(imagePath)
	if err != nil {
		logger.Error("Failed to get image file info: " + err.Error())
		return "", err
	}

	// 生成唯一的对象名称
	timestamp := time.Now().UnixNano()
	ext := filepath.Ext(imagePath)
	fileName := fmt.Sprintf("frame_%d%s", timestamp, ext)
	objectName := fmt.Sprintf("%s%s", objectPrefix, fileName)

	// 上传文件
	uploadInfo, err := client.PutObject(ctx, bucketName, objectName, src, fileInfo.Size(), minio.PutObjectOptions{
		ContentType: "image/jpeg",
	})

	if err != nil {
		logger.Error("Failed to upload image file: " + err.Error())
		return "", err
	}

	logger.Info("Image file uploaded successfully", zap.String("objectName", objectName), zap.Int64("size", uploadInfo.Size))

	// 生成可访问的URL
	url, err := GetFileURL(ctx, objectName)
	if err != nil {
		return "", err
	}

	return url, nil
}

// 分片上传相关常量
const (
	// DefaultMultipartSize 默认分片大小为5MB
	DefaultMultipartSize = 5 * 1024 * 1024
	// DefaultMultipartThreshold 超过此大小的文件将使用分片上传
	DefaultMultipartThreshold = 64 * 1024 * 1024 // 64MB
)

// MultipartUploadFile 使用MinIO的自动分片上传功能上传大文件
func MultipartUploadFile(ctx context.Context, file *multipart.FileHeader, objectPrefix string) (string, error) {
	if client == nil {
		return "", fmt.Errorf("MinIO client is not initialized")
	}

	// 打开文件
	src, err := file.Open()
	if err != nil {
		logger.Error("Failed to open file: " + err.Error())
		return "", err
	}
	defer src.Close()

	// 生成唯一的对象名称
	timestamp := time.Now().UnixNano()
	ext := filepath.Ext(file.Filename)
	fileName := strings.Replace(strings.TrimSuffix(file.Filename, ext), " ", "", -1)
	objectName := fmt.Sprintf("%s%s_%d%s", objectPrefix, fileName, timestamp, ext)

	// 使用MinIO的PutObject函数，它会自动处理分片上传
	// 对于大文件，MinIO客户端会自动进行分片上传
	uploadInfo, err := client.PutObject(ctx, bucketName, objectName, src, file.Size, minio.PutObjectOptions{
		ContentType: "application/octet-stream",
		PartSize:    DefaultMultipartSize, // 设置分片大小
	})
	if err != nil {
		logger.Error("Failed to upload file: " + err.Error())
		return "", err
	}

	logger.Info("File uploaded successfully", zap.String("objectName", objectName), zap.Int64("size", uploadInfo.Size))

	// 生成可访问的URL
	url, err := GetFileURL(ctx, objectName)
	if err != nil {
		return "", err
	}

	return url, nil
}
